Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix tree view reveal() behavior #8783

Merged
merged 1 commit into from
Dec 15, 2020
Merged

Fix tree view reveal() behavior #8783

merged 1 commit into from
Dec 15, 2020

Conversation

amiramw
Copy link
Member

@amiramw amiramw commented Nov 26, 2020

Signed-off-by: Amiram Wingarten amiram.wingarten@sap.com

What it does

Various fixes to support tree view reveal in several unsupported scenarios and align it with vscode.

  • reveal called without focus now opens the view container (left pane) and view (section), without focus
  • reveal of a node that its ancestors are collapsed now expand all parents and reveals it
  • reveal of a node that was not cached by backend or not fetched by client (for example when showing the tree for the first time) now works
  • reveal of a tree element that is not identical (===) to cached node now works

How to test

Scenarios

First time:

  1. Open theia from chrome incognito (like first time).
  2. F1 -> Reveal c New Object or F1 -> Reveal c Same Object
  3. Test view section is opened, a and b are expanded and c is selected, before this PR nothing happened

From other view container:

  1. Go to debug pane.
  2. F1 -> Reveal c New Object or F1 -> Reveal c Same Object
  3. Explorer is opened. Test view section is opened, a and b are expanded and c is selected, before this PR nothing happened.

From other view container:

  1. Go to debug pane.
  2. F1 -> Reveal c New Object or F1 -> Reveal c Same Object
  3. Explorer is opened. Test view section is opened, a and b are expanded and c is selected, before this PR nothing happened.

With non identical element:

  1. Open test view
  2. Expand the tree to show c and then collapse it
  3. F1 -> Reveal c New Object or F1 -> Reveal c Same Object
  4. Tree is expanded and c is selected. Before this PR only "Reveal c Same Object" worked

Review checklist

Reminder for reviewers

@amiramw
Copy link
Member Author

amiramw commented Dec 7, 2020

@kittaakos @vince-fugnitto do one of you has the time to review this? 🙏

@kittaakos
Copy link
Contributor

@amiramw, I'll look into the changes today. I tried it yesterday and it worked. 👍 Regarding the API; instead of the paren-chain string array we pass around, can we support the same API as VS Code does?

$reveal(treeViewId: string, itemInfo: { item: ITreeItem, parentChain: ITreeItem[] } | undefined, options: IRevealOptions): Promise<void>;

Do you know why cannot we have a reference to the treeDataProvider in plugin-view-registry.ts? Calculating the parent chain in place would be much better. What do you think?

@vince-fugnitto vince-fugnitto added plug-in system issues related to the plug-in system tree issues related to the tree (ex: tree widget) vscode issues related to VSCode compatibility labels Dec 8, 2020
@amiramw
Copy link
Member Author

amiramw commented Dec 8, 2020

Regarding the API; instead of the paren-chain string array we pass around, can we support the same API as VS Code does?

$reveal(treeViewId: string, itemInfo: { item: ITreeItem, parentChain: ITreeItem[] } | undefined, options: IRevealOptions): Promise<void>;

I don't think our tree handling is exact copy of vscode. I did see the parent chain and thought that we need it too. But currently there are a lot of fields in ITreeItem that are not relevant to the interface.

Do you know why cannot we have a reference to the treeDataProvider in plugin-view-registry.ts? Calculating the parent chain in place would be much better. What do you think?

Because treeDataProvider is defined in the plugin host process and plugin-view-registry.ts is on the browser. So there can't be in place calls to it.

Comment on lines 278 to 285
private getTreeItemLabel(treeItem: TreeItem2): string | undefined {
let label: string | undefined;
const treeItemLabel: string | TreeItemLabel | undefined = treeItem.label;
if (typeof treeItemLabel === 'object' && typeof treeItemLabel.label === 'string') {
label = treeItemLabel.label;
} else {
label = treeItem.label;
}
return label;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please simplify:

    private getTreeItemLabel(treeItem: TreeItem2): string | undefined {
        const treeItemLabel: string | TreeItemLabel | undefined = treeItem.label;
        if (typeof treeItemLabel === 'object' && typeof treeItemLabel.label === 'string') {
            return treeItemLabel.label;
        }
        return treeItem.label;
    }

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

return idLabel;
}

private buildTreeItemId(parentId: string, index: number, idLabel?: string) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing return type.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is idLabel optional?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed return type.

Changed idLabel to be string | undefined. By vscode API doc id or label must exist but by typescript typing and by developer mistake it means that it can be undefined. in this case I don't mind to keep the value "undefined" in the generated id.

Comment on lines 249 to 254
const parent = this.treeDataProvider.getParent && await this.treeDataProvider.getParent(element);
const chain = await this.calculateRevealParentChain(parent);
if (!chain || chain.length === 0) {
// parents are inconsistent
return;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please help me to understand this flow, I must be overlooking something:

  • the treeDataProvider supports getParent,
  • you have any invalid ID, so the parent will resolve to undefined.
  • you call calculateRevealParentChain recursively with undefined, but that will return with an array with an empty string (chain.length !== 0). Is that expected? Why is it needed to have the empty string. I do not understand that part. Code returns with an empty array.

https://github.com/microsoft/vscode/blob/1aa76d792f6857a0444b71e0a169f34a8ef5488d/src/vs/workbench/api/common/extHostTreeViews.ts#L394

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed now so technical root doesn't get an entry in the array

if (treeItem.id) {
return chain.concat(treeItem.id);
}
const idLabel = this.getTreeItemIdLabel(treeItem);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should go under if (!children)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

}
}
// couldn't calculate consistent parent chain and id
return;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's be specific: return undefined. Please fix other places too.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

private async calculateRevealParentChain(element: T | undefined): Promise<string[] | undefined> {
if (!element) {
// root
return [''];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As noted below; why is it [''] instead of []. I do not see where the empty string ID is used. Please explain.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is built with '' as root id here:

const root: CompositeTreeNode & ExpandableTreeNode = {
id: '',
parent: undefined,
name: '',
visible: false,
expanded: true,
children: []
};

Also in this file getChildren receives a string parent id and treats empty string as root.

But now that I think of it this technical root is always revealed and expanded so I removed it from the array.

if (elementId) {
return this.proxy.$reveal(this.treeViewId, elementId, {
const elementParentChain = await this.calculateRevealParentChain(element);
if (elementParentChain) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What should be revealed when the elementParentChain is ['']?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The root. However i don't think you can trigger a call only with the technical root from vscode API as you get a compile error if you don't pass truthy argument to reveal.

Anyway I removed '' from the parent chain.

@@ -98,14 +103,17 @@ export class TreeViewsMainImpl implements TreeViewsMain, Disposable {
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
async $reveal(treeViewId: string, treeItemId: string, options: TreeViewRevealOptions): Promise<any> {
const viewPanel = await this.viewRegistry.openView(treeViewId, { activate: options.focus });
async $reveal(treeViewId: string, elementParentChain: string[], options: TreeViewRevealOptions): Promise<any> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a single line of comment on what elementParentChain is. The original treeItemId was obvious. Thanks!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Copy link
Contributor

@tsmaeder tsmaeder Dec 15, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

May I suggest a rename to "ancestorItemIds"

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kittaakos
Copy link
Contributor

@amiramw, I added a few remarks. Please adjust. The behavior works, but I will verify once more.

I do not know if we have to note the breaking API change in the changelog, but doing it should not hurt. Please do so. Thanks!

@amiramw
Copy link
Member Author

amiramw commented Dec 10, 2020

I do not know if we have to note the breaking API change in the changelog, but doing it should not hurt. Please do so. Thanks!

@kittaakos I fixed all the code comments. Which API do you mean?

@kittaakos
Copy link
Contributor

I fixed all the code comments.

Thank you! 👍

Which API do you mean?

I meant this one. The second arg was a string.

async $reveal(treeViewId: string, elementParentChain: string[], options: TreeViewRevealOptions): Promise<any>

I leave the decision to you.

@amiramw
Copy link
Member Author

amiramw commented Dec 10, 2020

@kittaakos I added the changelog as well

Copy link
Contributor

@kittaakos kittaakos left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please proceed with the merge, @amiramw 👍

(Two explicit return undefined is missing, though.)

Various fixes to support tree view reveal in several unsupported scenarios and align it with vscode.

- reveal called without focus now opens the view container (left pane) and view (section), without focus
- reveal of a node that its ancestors are collapsed now expand all parents and reveals it
- reveal of a node that was not cached by backend or not fetched by client (for example when showing the tree for the first time) now works

Signed-off-by: Amiram Wingarten <amiram.wingarten@sap.com>
@amiramw
Copy link
Member Author

amiramw commented Dec 15, 2020

Please proceed with the merge, @amiramw 👍

(Two explicit return undefined is missing, though.)

Fixed. I'll merge once builds finish.

@amiramw amiramw merged commit 9e950a8 into master Dec 15, 2020
@amiramw amiramw deleted the reveal branch December 15, 2020 12:17
@github-actions github-actions bot added this to the 1.9.0 milestone Dec 15, 2020
amiramw added a commit that referenced this pull request Jan 5, 2021
The issue is a regression introduced in #8783
Calculation of parent chain (for id calculation) calls getChildren which dispose nodes.
This means that reveal for a node that is already revealed dispose the node and so the frontend has an id that doesn't exist in the backend anymore so tree item command is not found.

The fix is to check the cache before calling getChildren and making sure that the technical root is also in the cache.

Signed-off-by: Amiram Wingarten <amiram.wingarten@sap.com>
amiramw added a commit that referenced this pull request Jan 5, 2021
The issue is a regression introduced in #8783
Calculation of parent chain (for id calculation) calls getChildren which dispose nodes.
This means that reveal for a node that is already revealed dispose the node and so the frontend has an id that doesn't exist in the backend anymore so tree item command is not found.

The fix is to check the cache before calling getChildren and making sure that the technical root is also in the cache.

Signed-off-by: Amiram Wingarten <amiram.wingarten@sap.com>
amiramw added a commit that referenced this pull request Jan 22, 2021
The issue is a regression introduced in #8783
Calculation of parent chain (for id calculation) calls getChildren which dispose nodes.
This means that reveal for a node that is already revealed dispose the node and so the frontend has an id that doesn't exist in the backend anymore so tree item command is not found.

The fix is to check the cache before calling getChildren and making sure that the technical root is also in the cache.

Signed-off-by: Amiram Wingarten <amiram.wingarten@sap.com>
@tsmaeder tsmaeder mentioned this pull request May 5, 2023
1 task
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
plug-in system issues related to the plug-in system tree issues related to the tree (ex: tree widget) vscode issues related to VSCode compatibility
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants